/*
   Copyright 2010 Johan Hilding

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package org.johanhil.flygtider.flightwatch;

import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import org.johanhil.flygtider.FlightInfoActivity;
import org.johanhil.flygtider.provider.FlightInfo;
import org.johanhil.flygtider.provider.FlightSearch;
import org.johanhil.flygtider.provider.FlightStatus;
import org.johanhil.flygtider.provider.impl.LFVFlightSearch;

import android.R;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.text.format.Time;
import android.util.Log;

public class FlightWatchBackgroundService extends Service
{
    private final String TAG = "FlightWatchBackgroundService";
    private static boolean isRunning = false;
    private Timer updateTimer;
    public static Integer taskDelay = 5; // in minutes
    
    public void onCreate()
    {
        super.onCreate();
        Log.d(TAG, "Service started");
        
        updateTimer = new Timer("UpdateTimer", true); // true or false... does it matter?
        
        updateTimer.scheduleAtFixedRate(
                new FetchAndNotifyIfUpdatedTimerTask(getApplicationContext()), 
                0, 
                taskDelay*60*1000);
        isRunning = true;
    }
    
    public IBinder onBind(Intent arg0)
    {
        return null;
    }
    
    @Override
    public void onDestroy()
    {
        super.onDestroy();
        Log.d(TAG, "Service stopped.");
        updateTimer.cancel();

        isRunning = false;
    }
    
    /**
     * Determines whether the service is actually running or not.
     *
     * @return true if the service is running. False if it is not.
     */
    public static boolean isRunning()
    {
        return isRunning;
    }
}

class FetchAndNotifyIfUpdatedTimerTask extends TimerTask
{
    private FlightWatcher flightWatcher;
    private Context context;
    private NotificationManager notificationManager;
    private HashMap<String, Integer> minutesToNextCheck;
    private String TAG = "FlightWatchBackgroundService";

    private int ticker = 0;
    
    public FetchAndNotifyIfUpdatedTimerTask(Context context)
    {
        this.context = context;
        this.notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        this.minutesToNextCheck = new HashMap<String, Integer>();
    }

    @Override
    public void run()
    {        
        flightWatcher = new FlightWatcher(context);
        List<FlightInfo> watchedFlights = flightWatcher.getWatchedFlights();
        FlightSearch flightSearcher = new LFVFlightSearch(context);
        
        // see if the notes for any of the flights differ,
        // and in that case notify the user
        Log.d(TAG, "# of watched flights " + watchedFlights.size());
        for (FlightInfo oldFlightInfo : watchedFlights)
        {
            String identifier = oldFlightInfo.getDate() + oldFlightInfo.getFlightNr();
            Log.d(TAG, "Checking updates for flight " + oldFlightInfo.getFlightNr());
            FlightInfo newFlightInfo = null;
            
            if (! minutesToNextCheck.containsKey(identifier))
            {
                newFlightInfo = flightSearcher.getFlight(oldFlightInfo.getIdentifyingAirport(), oldFlightInfo.isArriving(), oldFlightInfo.getFlightNr(), oldFlightInfo.getDate());
            }
            else if ((minutesToNextCheck.get(identifier) - FlightWatchBackgroundService.taskDelay) <= 0)
            {
                newFlightInfo = flightSearcher.getFlight(oldFlightInfo.getIdentifyingAirport(), oldFlightInfo.isArriving(), oldFlightInfo.getFlightNr(), oldFlightInfo.getDate());
            }
            
            // if we don't update this time, we just update the time until next update... nggh description :-D
            if (newFlightInfo == null)
            {
                minutesToNextCheck.put(identifier, minutesToNextCheck.get(identifier) - FlightWatchBackgroundService.taskDelay);
                continue;
            }            

            if (! oldFlightInfo.getNotes().equals(newFlightInfo.getNotes()))
            {
                Log.d(TAG, "Flight " + oldFlightInfo.getFlightNr() + " changed!");
                Log.d(TAG, "Old text: " + oldFlightInfo.getNotes());
                Log.d(TAG, "New text: " + newFlightInfo.getNotes());
                notifyChange(newFlightInfo);
                flightWatcher.add(newFlightInfo); // updates
            }
            
            setNewNextCheckTime(newFlightInfo);
        }
        
        flightWatcher.close();
    }
    
    private void setNewNextCheckTime(FlightInfo newFlightInfo)
    {
        // if closer than 30 minutes, then check every iteration (so every 5 minutes).
        // if further away, then check every 15 minutes.
        // if really far away (8h+), check every 30 minutes.
        String identifier = newFlightInfo.getDate() + newFlightInfo.getFlightNr();

        Time now = new Time();
        now.setToNow();
        
        Time latestUpdatedTime = FlightStatus.getStatus(newFlightInfo).getNewTime();
        
        if (latestUpdatedTime != null)
        {
            long diff = (latestUpdatedTime.minute + latestUpdatedTime.hour * 60)
                    - (now.minute + now.hour * 60);

            if (diff < 30)
            {
                // < 30m
                minutesToNextCheck.put(identifier, 5);
            } else if (diff > 8 * 60)
            {
                // > 8h
                minutesToNextCheck.put(identifier, 30);
            } else
            {
                // >30m && <8h
                minutesToNextCheck.put(identifier, 15);
            }
        }
    }

    private void notifyChange(FlightInfo info)
    {
        Log.i(TAG, "Notifying user of flight change for flight " + info.getFlightNr());
        if (notificationManager != null)
        {
            Notification notification = new Notification(
                    R.drawable.stat_sys_warning, 
                    "Flight " + info.getFlightNr(), 
                    System.currentTimeMillis());
            
            notification.flags = notification.flags | Notification.FLAG_AUTO_CANCEL;
            notification.defaults = notification.defaults | Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE;
            
            Intent flightInfoActivity = new Intent(context, FlightInfoActivity.class);
            flightInfoActivity.putExtra("flightinfo", info);
            flightInfoActivity.putExtra("refresh", true);
            PendingIntent contentIntent = PendingIntent.getActivity(context, 0, flightInfoActivity, PendingIntent.FLAG_UPDATE_CURRENT);
            
            String title = formatTitle(info);
            String text = formatText(info);
            
            notification.setLatestEventInfo(context, title, text, contentIntent);
            
            notificationManager.notify(ticker, notification);
            ticker += 1;
        }
        else
        {
            Log.d("FetchAndNotifyIfUpdatedTimerTask", "notification manager null, cannot notify");
        }
    }
    
    public String formatTitle(FlightInfo info)
    {
        return "Flight " + info.getFlightNr();
    }
    
    public String formatText(FlightInfo info)
    {
        return "Uppdaterad status om flight " + info.getFlightNr();   
    }
}
